﻿/**
************************************************************************************************************************
* @file    CPU_FMC.c
* @author  CTY
* @version 1.0
* @date    2020/3/1
* @brief   FMC总线驱动
************************************************************************************************************************
* @attention
*
*                               版权所有 (c),  2015,  贵阳永青仪电科技有限公司
* \n
* 
@verbatim
    （详细内容）
@endverbatim
* 
************************************************************************************************************************
*/ 
/*====================================================================================================================*/
/*                                                      引用文件                                                      */
/*====================================================================================================================*/
#include "stm32h7xx_hal.h"


/*====================================================================================================================*/
/*                                                      常量定义                                                      */
/*====================================================================================================================*/

/// 扩展内存地址定义
const uint32_t EX_MEM_ADDR = 0xC0000000;

/// 扩展内存大小定义
const uint32_t EX_MEM_SIZE = 0x02000000;


/*====================================================================================================================*/
/*                                                     本地宏定义                                                     */
/*====================================================================================================================*/

/// SDRAM 操作超时值
#define SDRAM_TIMEOUT           ((uint32_t)0xFFFF)

#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200)


/*====================================================================================================================*/
/*                                                  本地数据类型定义                                                  */
/*====================================================================================================================*/


/*====================================================================================================================*/
/*                                                      全局变量                                                      */
/*====================================================================================================================*/


/*====================================================================================================================*/
/*                                                      本地变量                                                      */
/*====================================================================================================================*/
SRAM_HandleTypeDef hsram1;
NAND_HandleTypeDef hnand1;
SDRAM_HandleTypeDef hsdram1;
SDRAM_HandleTypeDef hsdram2;

static uint32_t FMC_Initialized = 0;
static uint32_t FMC_DeInitialized = 0;


/*====================================================================================================================*/
/*                                                    本地函数声明                                                    */
/*====================================================================================================================*/
static void _FMC_MspInit(void);
static void _FMC_MspDeInit(void);
static void CPU_FMC_Init_Sram1();
static void CPU_FMC_Init_Sdram1();
static void CPU_FMC_Init_Sdram2();
static void CPU_FMC_Init_Nand1();


/*====================================================================================================================*/
/*                                                      全局函数                                                      */
/*====================================================================================================================*/

/**************************************************************************************************/
/**
@brief      初始化
@verbatim   
    （详细描述）
@endverbatim
@param      无
@retval     true=成功，false=失败。
*/
/**************************************************************************************************/
void CPU_FMC_Init(void)
{
    CPU_FMC_Init_Sram1();
    CPU_FMC_Init_Sdram1();
    //CPU_FMC_Init_Sdram2();
    //CPU_FMC_Init_Nand1();
}


void HAL_SRAM_MspInit(SRAM_HandleTypeDef* sramHandle)
{
  _FMC_MspInit();
}

void HAL_SRAM_MspDeInit(SRAM_HandleTypeDef* sramHandle)
{
  _FMC_MspDeInit();
}

void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* sdramHandle)
{
  _FMC_MspInit();
}

void HAL_SDRAM_MspDeInit(SDRAM_HandleTypeDef* sdramHandle)
{
  _FMC_MspDeInit();
}

void HAL_NAND_MspInit(NAND_HandleTypeDef* nandHandle)
{
  _FMC_MspInit();
}

void HAL_NAND_MspDeInit(NAND_HandleTypeDef* nandHandle)
{
  _FMC_MspDeInit();
}


/*====================================================================================================================*/
/*                                                      本地函数                                                      */
/*====================================================================================================================*/

static void _FMC_MspInit(void){
  /* USER CODE BEGIN FMC_MspInit 0 */

  /* USER CODE END FMC_MspInit 0 */
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if (FMC_Initialized) {
    return;
  }
  FMC_Initialized = 1;

  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_ENABLE();

  /** FMC GPIO Configuration
  PI6   ------> FMC_D28
  PI5   ------> FMC_NBL3
  PI4   ------> FMC_NBL2
  PG9   ------> FMC_NCE
  PD5   ------> FMC_NWE
  PD4   ------> FMC_NOE
  PI1   ------> FMC_D25
  PI0   ------> FMC_D24
  PI7   ------> FMC_D29
  PE1   ------> FMC_NBL1
  PD6   ------> FMC_NWAIT
  PI2   ------> FMC_D26
  PH15   ------> FMC_D23
  PH14   ------> FMC_D22
  PE0   ------> FMC_NBL0
  PD7   ------> FMC_NE1
  PI3   ------> FMC_D27
  PG15   ------> FMC_SDNCAS
  PD0   ------> FMC_D2
  PH13   ------> FMC_D21
  PI9   ------> FMC_D30
  PD1   ------> FMC_D3
  PI10   ------> FMC_D31
  PG8   ------> FMC_SDCLK
  PF2   ------> FMC_A2
  PF1   ------> FMC_A1
  PF0   ------> FMC_A0
  PG5   ------> FMC_BA1
  PF3   ------> FMC_A3
  PG4   ------> FMC_BA0
  PG2   ------> FMC_A12
  PF5   ------> FMC_A5
  PF4   ------> FMC_A4
  PH2   ------> FMC_SDCKE0
  PE10   ------> FMC_D7
  PH3   ------> FMC_SDNE0
  PH5   ------> FMC_SDNWE
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PE9   ------> FMC_D6
  PE11   ------> FMC_D8
  PH10   ------> FMC_D18
  PH11   ------> FMC_D19
  PD15   ------> FMC_D1
  PD14   ------> FMC_D0
  PF12   ------> FMC_A6
  PF15   ------> FMC_A9
  PE12   ------> FMC_D9
  PE15   ------> FMC_D12
  PH9   ------> FMC_D17
  PH12   ------> FMC_D20
  PD11   ------> FMC_CLE
  PD12   ------> FMC_ALE
  PF11   ------> FMC_SDNRAS
  PG0   ------> FMC_A10
  PE8   ------> FMC_D5
  PE13   ------> FMC_D10
  PH6   ------> FMC_SDNE1
  PH8   ------> FMC_D16
  PD10   ------> FMC_D15
  PD9   ------> FMC_D14
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE14   ------> FMC_D11
  PH7   ------> FMC_SDCKE1
  PD8   ------> FMC_D13
  */
  /* GPIO_InitStruct */
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;

  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_1
                          |GPIO_PIN_0|GPIO_PIN_7|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_9|GPIO_PIN_10;
  HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_15|GPIO_PIN_8|GPIO_PIN_5
                          |GPIO_PIN_4|GPIO_PIN_2|GPIO_PIN_0|GPIO_PIN_1;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_7
                          |GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_15|GPIO_PIN_14
                          |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_8;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_9
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_13|GPIO_PIN_7|GPIO_PIN_14;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_15|GPIO_PIN_14|GPIO_PIN_13|GPIO_PIN_2
                          |GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_9|GPIO_PIN_12|GPIO_PIN_6|GPIO_PIN_8
                          |GPIO_PIN_7;
  HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_3
                          |GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_11;
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USER CODE BEGIN FMC_MspInit 1 */

  /* USER CODE END FMC_MspInit 1 */
}

static void _FMC_MspDeInit(void){
  /* USER CODE BEGIN FMC_MspDeInit 0 */

  /* USER CODE END FMC_MspDeInit 0 */
  if (FMC_DeInitialized) {
    return;
  }
  FMC_DeInitialized = 1;
  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_DISABLE();

  /** FMC GPIO Configuration
  PI6   ------> FMC_D28
  PI5   ------> FMC_NBL3
  PI4   ------> FMC_NBL2
  PG9   ------> FMC_NCE
  PD5   ------> FMC_NWE
  PD4   ------> FMC_NOE
  PI1   ------> FMC_D25
  PI0   ------> FMC_D24
  PI7   ------> FMC_D29
  PE1   ------> FMC_NBL1
  PD6   ------> FMC_NWAIT
  PI2   ------> FMC_D26
  PH15   ------> FMC_D23
  PH14   ------> FMC_D22
  PE0   ------> FMC_NBL0
  PD7   ------> FMC_NE1
  PI3   ------> FMC_D27
  PG15   ------> FMC_SDNCAS
  PD0   ------> FMC_D2
  PH13   ------> FMC_D21
  PI9   ------> FMC_D30
  PD1   ------> FMC_D3
  PI10   ------> FMC_D31
  PG8   ------> FMC_SDCLK
  PF2   ------> FMC_A2
  PF1   ------> FMC_A1
  PF0   ------> FMC_A0
  PG5   ------> FMC_BA1
  PF3   ------> FMC_A3
  PG4   ------> FMC_BA0
  PG2   ------> FMC_A12
  PF5   ------> FMC_A5
  PF4   ------> FMC_A4
  PH2   ------> FMC_SDCKE0
  PE10   ------> FMC_D7
  PH3   ------> FMC_SDNE0
  PH5   ------> FMC_SDNWE
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PE9   ------> FMC_D6
  PE11   ------> FMC_D8
  PH10   ------> FMC_D18
  PH11   ------> FMC_D19
  PD15   ------> FMC_D1
  PD14   ------> FMC_D0
  PF12   ------> FMC_A6
  PF15   ------> FMC_A9
  PE12   ------> FMC_D9
  PE15   ------> FMC_D12
  PH9   ------> FMC_D17
  PH12   ------> FMC_D20
  PD11   ------> FMC_CLE
  PD12   ------> FMC_ALE
  PF11   ------> FMC_SDNRAS
  PG0   ------> FMC_A10
  PE8   ------> FMC_D5
  PE13   ------> FMC_D10
  PH6   ------> FMC_SDNE1
  PH8   ------> FMC_D16
  PD10   ------> FMC_D15
  PD9   ------> FMC_D14
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE14   ------> FMC_D11
  PH7   ------> FMC_SDCKE1
  PD8   ------> FMC_D13
  */

  HAL_GPIO_DeInit(GPIOI, GPIO_PIN_6|GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_1
                          |GPIO_PIN_0|GPIO_PIN_7|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_9|GPIO_PIN_10);

  HAL_GPIO_DeInit(GPIOG, GPIO_PIN_9|GPIO_PIN_15|GPIO_PIN_8|GPIO_PIN_5
                          |GPIO_PIN_4|GPIO_PIN_2|GPIO_PIN_0|GPIO_PIN_1);

  HAL_GPIO_DeInit(GPIOD, GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_6|GPIO_PIN_7
                          |GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_15|GPIO_PIN_14
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_10|GPIO_PIN_9
                          |GPIO_PIN_8);

  HAL_GPIO_DeInit(GPIOE, GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_9
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_13|GPIO_PIN_7|GPIO_PIN_14);

  HAL_GPIO_DeInit(GPIOH, GPIO_PIN_15|GPIO_PIN_14|GPIO_PIN_13|GPIO_PIN_2
                          |GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_9|GPIO_PIN_12|GPIO_PIN_6|GPIO_PIN_8
                          |GPIO_PIN_7);

  HAL_GPIO_DeInit(GPIOF, GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_3
                          |GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_11);

  /* USER CODE BEGIN FMC_MspDeInit 1 */

  /* USER CODE END FMC_MspDeInit 1 */
}

/**************************************************************************************************/
/**
@brief      SDRAM 初始化序列
@verbatim   
    （详细注解）
@endverbatim
@param      sdramHandle: 句柄
@param      CommandTarget: 命令
@retval     无
*/
/**************************************************************************************************/
static void _SDRAM_Initialization_sequence(SDRAM_HandleTypeDef *sdramHandle, uint32_t CommandTarget)
{
    static FMC_SDRAM_CommandTypeDef Command;
    __IO uint32_t tmpmrd =0;
    
    /* Step 1:  Configure a clock configuration enable command */
    Command.CommandMode             = FMC_SDRAM_CMD_CLK_ENABLE;
    Command.CommandTarget           = CommandTarget;
    Command.AutoRefreshNumber       = 1;
    Command.ModeRegisterDefinition  = 0;
    HAL_SDRAM_SendCommand(sdramHandle, &Command, SDRAM_TIMEOUT);
    
    /* Step 2: Insert 100 us minimum delay */ 
    /* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
    HAL_Delay(1);
    
    /* Step 3: Configure a PALL (precharge all) command */ 
    Command.CommandMode             = FMC_SDRAM_CMD_PALL;
    Command.CommandTarget           = CommandTarget;
    Command.AutoRefreshNumber       = 1;
    Command.ModeRegisterDefinition  = 0;
    HAL_SDRAM_SendCommand(sdramHandle, &Command, SDRAM_TIMEOUT);  
    
    /* Step 4: Configure an Auto Refresh command */ 
    Command.CommandMode             = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
    Command.CommandTarget           = CommandTarget;
    Command.AutoRefreshNumber       = 8;
    Command.ModeRegisterDefinition  = 0;
    HAL_SDRAM_SendCommand(sdramHandle, &Command, SDRAM_TIMEOUT);
    
    /* Step 5: Program the external memory mode register */
    tmpmrd = (uint32_t) SDRAM_MODEREG_BURST_LENGTH_1          |
                        SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
                        SDRAM_MODEREG_CAS_LATENCY_3           |
                        SDRAM_MODEREG_OPERATING_MODE_STANDARD |
                        SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
    
    Command.CommandMode             = FMC_SDRAM_CMD_LOAD_MODE;
    Command.CommandTarget           = CommandTarget;
    Command.AutoRefreshNumber       = 1;
    Command.ModeRegisterDefinition  = tmpmrd;
    HAL_SDRAM_SendCommand(sdramHandle, &Command, SDRAM_TIMEOUT);
    
    /* Step 6: Set the refresh rate counter */
    /* Set the device refresh rate */
    HAL_SDRAM_ProgramRefreshRate(sdramHandle, 1855);
}

/**************************************************************************************************/
/**
@brief      SDRAM 数据测试
@verbatim   
    （详细注解）
@endverbatim
@param      无
@retval     true=成功，false=失败。
*/
/**************************************************************************************************/
static void _TestExtSDRAM(uint32_t addr, uint32_t size)
{
    uint8_t *p8 = (uint8_t *)addr;
    uint32_t *p32 = (uint32_t *)(addr + size) - 4;
    uint8_t buff[4] = { 0x55, 0xA5, 0x5A, 0xAA };
    
    // 写入
    for(int i = 0 ; i < 4 ; i++) {
        p8[i] = buff[i];
        p32[i] = (uint32_t)buff[i];
    }
    
    // 读出
    for(int i = 0 ; i < 4 ; i++) {
        while (p8[i] != buff[i]);
        while (p32[i] != (uint32_t)buff[i]) ;
    }
}

static void CPU_FMC_Init_Sram1(void)
{
    /** Perform the SRAM1 memory initialization sequence
    */
    hsram1.Instance = FMC_NORSRAM_DEVICE;
    hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
    /* hsram1.Init */
    hsram1.Init.NSBank             = FMC_NORSRAM_BANK1;              /* 使用的BANK1，即使用的片选FMC_NE1 */
    hsram1.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;   /* 禁止地址数据复用 */
    hsram1.Init.MemoryType         = FMC_MEMORY_TYPE_SRAM;           /* 存储器类型SRAM */
    hsram1.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_8;    /* 32位总线宽度 */
    hsram1.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;  /* 关闭突发模式 */
    hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;   /* 用于设置等待信号的极性，关闭突发模式，此参数无效 */
    hsram1.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;      /* 关闭突发模式，此参数无效 */
    hsram1.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;     /* 用于使能或者禁止写保护 */
    hsram1.Init.WaitSignal         = FMC_WAIT_SIGNAL_DISABLE;        /* 关闭突发模式，此参数无效 */
    hsram1.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;      /* 禁止扩展模式 */
    hsram1.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 用于异步传输期间，使能或者禁止等待信号，这里选择关闭 */
    hsram1.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;        /* 禁止写突发 */
    hsram1.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 仅同步模式才做时钟输出 */
    hsram1.Init.WriteFifo          = FMC_WRITE_FIFO_ENABLE;          /* 使能写FIFO */
    
    /* FMC使用的HCLK3，主频200MHz，1个FMC时钟周期就是5ns */
    FMC_NORSRAM_TimingTypeDef Timing = { 0 };
    Timing.AccessMode             = FMC_ACCESS_MODE_A;
    Timing.AddressSetupTime       = 0;      // 地址建立时间，范围 0 - 15 个FMC时钟周期个数
    Timing.DataSetupTime          = 20;     // 数据保持时间，范围 1 -255 个时钟周期个数
    while(HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK);
}

static void CPU_FMC_Init_Sdram1(void)
{
    hsdram1.Instance = FMC_SDRAM_DEVICE;
    hsdram1.Init.SDBank             = FMC_SDRAM_BANK1;               /* 硬件设计上用的BANK1 */
    hsdram1.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_9;   /* 9列 */
    hsdram1.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_12;     /* 12行 */
    hsdram1.Init.MemoryDataWidth    = FMC_SDRAM_MEM_BUS_WIDTH_32;    /* 32位带宽 */
    hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;  /* SDRAM有4个BANK */
    hsdram1.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_3;       /* CAS Latency可以设置Latency1，2和3，实际测试Latency3稳定 */
    hsdram1.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 禁止写保护 */
    hsdram1.Init.SDClockPeriod      = FMC_SDRAM_CLOCK_PERIOD_2;      /* FMC时钟200MHz，2分频后给SDRAM，即100MHz */
    hsdram1.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;       /* 使能读突发 */
    hsdram1.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_0;       /* 此位定CAS延时后延后多少个SDRAM时钟周期读取数据，实际测此位可以设置无需延迟 */
    
    // FMC使用的HCLK3时钟，200MHz，用于SDRAM的话，至少2分频，也就是100MHz，即1个SDRAM时钟周期是10ns
    FMC_SDRAM_TimingTypeDef Timing;
    Timing.LoadToActiveDelay    = 2;    // 14ns
    Timing.ExitSelfRefreshDelay = 7;    // 70ns
    Timing.SelfRefreshTime      = 5;    // 45ns
    Timing.RowCycleDelay        = 7;    // 70ns
    Timing.WriteRecoveryTime    = 2;    // 2 CLK
    Timing.RPDelay              = 2;    // 20ns
    Timing.RCDDelay             = 2;    // 20ns

    while(HAL_SDRAM_Init(&hsdram1, &Timing) != HAL_OK);

    _SDRAM_Initialization_sequence(&hsdram1, FMC_SDRAM_CMD_TARGET_BANK1);
    _TestExtSDRAM(EX_MEM_ADDR, EX_MEM_SIZE);
}

static void CPU_FMC_Init_Sdram2(void)
{
    hsdram2.Instance = FMC_SDRAM_DEVICE;
    hsdram2.Init.SDBank             = FMC_SDRAM_BANK2; /* 硬件设计上用的BANK2 */
    hsdram2.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_9; /* 9列 */
    hsdram2.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_13; /* 13行 */
    hsdram2.Init.MemoryDataWidth    = FMC_SDRAM_MEM_BUS_WIDTH_32; /* 32位带宽 */
    hsdram2.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; /* SDRAM有4个BANK */
    hsdram2.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_3; /* CAS Latency可以设置Latency1，2和3，实际测试Latency3稳定 */
    hsdram2.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 禁止写保护 */
    hsdram2.Init.SDClockPeriod      = FMC_SDRAM_CLOCK_PERIOD_2; /* FMC时钟200MHz，2分频后给SDRAM，即100MHz */
    hsdram2.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE; /* 使能读突发 */
    hsdram2.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_0; /* 此位定CAS延时后延后多少个SDRAM时钟周期读取数据，实际测此位可以设置无需延迟 */
    
    // FMC使用的HCLK3时钟，200MHz，用于SDRAM的话，至少2分频，也就是100MHz，即1个SDRAM时钟周期是10ns
    FMC_SDRAM_TimingTypeDef Timing;
    Timing.LoadToActiveDelay    = 2;     // 14ns
    Timing.ExitSelfRefreshDelay = 7;     // 70ns
    Timing.SelfRefreshTime      = 5;     // 45ns
    Timing.RowCycleDelay        = 7;     // 70ns
    Timing.WriteRecoveryTime    = 2;     // 2 CLK
    Timing.RPDelay              = 2;     // 20ns
    Timing.RCDDelay             = 2;     // 20ns

    while(HAL_SDRAM_Init(&hsdram2, &Timing) != HAL_OK);

    _SDRAM_Initialization_sequence(&hsdram2, FMC_SDRAM_CMD_TARGET_BANK2);
    _TestExtSDRAM(0xD0000000, 0x04000000);
}

static void CPU_FMC_Init_Nand1(void)
{
//    FMC_NAND_PCC_TimingTypeDef ComSpaceTiming = {0};
//    FMC_NAND_PCC_TimingTypeDef AttSpaceTiming = {0};
//
//    /** Perform the NAND1 memory initialization sequence
//    */
//    hnand1.Instance = FMC_NAND_DEVICE;
//    /* hnand1.Init */
//    hnand1.Init.NandBank = FMC_NAND_BANK3;
//    hnand1.Init.Waitfeature = FMC_NAND_WAIT_FEATURE_ENABLE;
//    hnand1.Init.MemoryDataWidth = FMC_NAND_MEM_BUS_WIDTH_8;
//    hnand1.Init.EccComputation = FMC_NAND_ECC_DISABLE;
//    hnand1.Init.ECCPageSize = FMC_NAND_ECC_PAGE_SIZE_256BYTE;
//    hnand1.Init.TCLRSetupTime = 0;
//    hnand1.Init.TARSetupTime = 0;
//    /* hnand1.Config */
//    hnand1.Config.PageSize = 0;
//    hnand1.Config.SpareAreaSize = 0;
//    hnand1.Config.BlockSize = 0;
//    hnand1.Config.BlockNbr = 0;
//    hnand1.Config.PlaneNbr = 0;
//    hnand1.Config.PlaneSize = 0;
//    hnand1.Config.ExtraCommandEnable = DISABLE;
//    /* ComSpaceTiming */
//    ComSpaceTiming.SetupTime = 252;
//    ComSpaceTiming.WaitSetupTime = 252;
//    ComSpaceTiming.HoldSetupTime = 252;
//    ComSpaceTiming.HiZSetupTime = 252;
//    /* AttSpaceTiming */
//    AttSpaceTiming.SetupTime = 252;
//    AttSpaceTiming.WaitSetupTime = 252;
//    AttSpaceTiming.HoldSetupTime = 252;
//    AttSpaceTiming.HiZSetupTime = 252;
//
//    if (HAL_NAND_Init(&hnand1, &ComSpaceTiming, &AttSpaceTiming) != HAL_OK)
//    {
//        return false;
//    }
}


/*====================================================================================================================*/
/*                                                     End of file                                                    */
/*====================================================================================================================*/
